/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c) 2002, GeoVISTA Center
 * Licensed under Simplified BSD License
 * This library is developed and distributed for free usage,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * You can redistribute and/or modify it under the terms of the
 * Simplified BSD License
 *
 *
 *
 *
 *
 *
 *
 * @original author: jin Chen 
 * @author: jin Chen 
 * @date: Jan 11, 2008$
 * @version: 1.0
 */
package edu.psu.geovista.app.vit.pub.components.sts;


import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.collections.set.ListOrderedSet;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.math.util.MathUtils;

import javax.swing.table.DefaultTableModel;
import java.util.*;

public class SSResult {//SSR
    public static final String Colname_ClusterID ="CLUSTER";
    public static final String Colname_RecordID ="LOC_ID";
    public static final String Colname_Latitude ="LATITUDE";
    public static final String Colname_Longitude ="LONGITUDE";
    public static final String Colname_Radius ="RADIUS";
    public static final String Colname_Size="SIZE";
    public static final String Colname_PVALUE="P_VALUE";
    public static final String Colname_ROE ="ODE";// 
    public static final String Colname_CLU_ODE ="CLU_ODE";// 
    public static final String Colname_LOCROE="LOC_ODE";// 
    public static final String Colname_CLU_Risk ="CLU_RISK";// 
    public static final String Colname_RRisk ="REL_RISK";// 
    public static final String Colname_ODESdv="ODE_StdDev";// 
    public static final String Colname_PctLowODE="Pct_NH";// 
    public static final String Colname_PctHODE="ODE_PctH";// 
    public static final String Colname_CLU_REL="ODE_CLU_RL";// 
    public static final String Colname_POP="Population";// 
    public static final String Colname_POPPct="Pct_Pop";// 

        Object sessionId; // 
        String fileGisdbf;
        MyTableModel dataGisdbf;//  
        MyTableModel dataColdbf;//  
        MyTableModel dataRRdbf;//  
        Set selClusterIds;// 
        Map<Object, SpatialClusterInfo> clusters;   // 
        boolean fillPopulationInfo;

        public SSResult(Object sessionId) {
            this.sessionId = sessionId;

        }


    public DefaultTableModel getDataRecordTable(){
            return  MyTableModel.getTableModel(dataGisdbf);
        }
         public DefaultTableModel getClusterRecordTable(){
             //Map<Object, Set> clusterGroup = getClusterGroup();
             //DefaultTableModel clusterTablemodel = compileClusterTableData(dataGisdbf, clusterGroup, new String[]{Colname_PVALUE, Colname_ROE, Colname_RRisk});
              //1. column names
             DefaultTableModel tmodel =new DefaultTableModel();
             // 
             String[] columnNames=new String[]{Colname_ClusterID,Colname_Size,Colname_PVALUE, Colname_ROE, Colname_RRisk,Colname_ODESdv,Colname_PctLowODE,Colname_PctHODE,Colname_Latitude,Colname_Longitude,Colname_Radius};
             if(this.isFillPopulationInfo()){
                  columnNames=new String[]{
                          Colname_ClusterID,Colname_Size,Colname_PVALUE, Colname_ROE, Colname_RRisk,
                          Colname_POP,Colname_POPPct,
                          Colname_ODESdv,Colname_PctLowODE,Colname_PctHODE,Colname_Latitude,Colname_Longitude,Colname_Radius};
             }
             class clusterInfo2Vector{
                 //convert a clusterinfo into a vector, the sequence of data matches to above columnNames
                 public Vector convert(SpatialClusterInfo cls){
                     Vector v=new Vector();
                     v.add(cls.id.Id);v.add(cls.memberIds.size());v.add(cls.pvalue);v.add(cls.ode);v.add(cls.rrisk);
                     v.add(cls.variance4ode); v.add(cls.pctLowOde); v.add(cls.pctHighOde);v.add(cls.latitude);v.add(cls.longitude);v.add(cls.radius);
                     return v;
                 }

                 /**
                  * convert addtional info: pop, pop_pct
                  * @param cls
                  * @return
                  */
                  public Vector convertFull(SpatialClusterInfo cls){
                     Vector v=new Vector();
                     v.add(cls.id.Id);v.add(cls.memberIds.size());v.add(cls.pvalue);v.add(cls.ode);v.add(cls.rrisk);
                     v.add(cls.population);v.add(cls.pop_percentage);
                     v.add(cls.variance4ode); v.add(cls.pctLowOde); v.add(cls.pctHighOde); v.add(cls.latitude);v.add(cls.longitude);v.add(cls.radius);
                     return v;
                 }
             }
             Vector colnames=new Vector();
             for (int i=0;i<columnNames.length ;i++){
                         colnames.add(columnNames[i]);
                     }
             tmodel.setColumnIdentifiers(colnames);

             //2.values
             clusterInfo2Vector cvt=new clusterInfo2Vector();
             Map<Object, SpatialClusterInfo> clusters = this.getClusterInfo();
             Set<Object> idset = clusters.keySet();
             ArrayList ids = new ArrayList(idset);
             Collections.sort(ids);
             for (Iterator iterator = ids.iterator(); iterator.hasNext();) {
                 Object id = (Object) iterator.next();
                 SpatialClusterInfo clusterInfo = clusters.get(id);

                 Vector arow = null;
                 if(this.isFillPopulationInfo()){
                     arow=cvt.convertFull(clusterInfo);
                 }
                 else{
                     arow= cvt.convert(clusterInfo);
                 }
                 tmodel.addRow(arow);
             }


             return tmodel;
         }
        public Map<Object, SpatialClusterInfo> getClusterInfo(){
            if(clusters==null){
                clusters=new HashMap<Object, SpatialClusterInfo>();
               readClusterInfo();
            }
            return clusters;
        }
        private void readClusterInfo(){
            Map<Object, Set> clsgrp = getClusterGroup();
            Object sesid = this.getSessionId();
            MyTableModel coldbf = this.getDataColdbf();
            // 
            // 
            int clscount = clsgrp.size();//coldbf.getRowCount();
            int IdIdx = coldbf.getColumnIndexById(Colname_ClusterID);
            int latIdx = coldbf.getColumnIndexById(Colname_Latitude);
            int logIdx = coldbf.getColumnIndexById(Colname_Longitude);
            int radiusIdx = coldbf.getColumnIndexById(Colname_Radius);
            int pvalueIdx = coldbf.getColumnIndexById(Colname_PVALUE);
            int roeIdx = coldbf.getColumnIndexById(Colname_ROE);
            int riskIdx = coldbf.getColumnIndexById(Colname_RRisk);


            for (int i=0;i<clscount;i++){
                Vector arow = coldbf.getRowValues(i);
                Object id = arow.get(IdIdx);//id within a session ( thus not include session id)
                SpatialClusterInfo cls=new SpatialClusterInfo(sesid,id);
                cls.latitude = (latIdx>=0)?(Double) arow.get(latIdx):Double.NaN;
                cls.longitude = (logIdx>=0)?(Double) arow.get(logIdx):Double.NaN;
                cls.radius= (radiusIdx>=0)?(Double) arow.get(radiusIdx):Double.NaN;
                cls.pvalue = (Double) arow.get(pvalueIdx);
                cls.ode= (Double) arow.get(roeIdx);
                cls.rrisk= (Double) arow.get(riskIdx);
                cls.memberIds = clsgrp.get(id);
                cls.circuleSize=getSaTScanPopulationSize();//this.getCircleSize();
               /* if(cls.memberIds ==null){
                    System.out.println("");
                }*/
                this.clusters.put(id,cls);
                //cls.variance4ode=getODEVariance_MeanNormalized(id);//getODEVariance(id);
                cls.pctLowOde=this.getPctSmallODE(id,1);
                cls.pctHighOde=this.getPctSignificantODE(id);
            }
        }
        private void getRelativeRiskInfo(){
            MyTableModel rrdbf = this.getDataRRdbf();
            this.setDataRRdbf(rrdbf);
        }

         /**
         *  
         *  
         * @param clusterId
         * @return
         */
       /* public double getODEVariance(Object clusterId){
             SpatialClusterInfo clusterInfo = this.clusters.get(clusterId);
             double expected = clusterInfo.ode;
             Set memberIds = clusterInfo.memberIds;
             double[] odes=new double[memberIds.size() ];
             int i=0;
             for (Iterator iterator = memberIds.iterator(); iterator.hasNext();) {
                 Object mbrId = (Object) iterator.next();
                 double loc_ode = (Double)dataGisdbf.getValue(Colname_LOCROE, Colname_RecordID, mbrId);
                 odes[i++]=loc_ode;
             }

             return JMathUtils.getStdDev(odes,expected);// 1;
        }*/

        

        /**
         * return the percentage of the total  number of counties that has ODE< the given <threshold>
         * @param clusterId
         * @param threshold
         * @return
         */
        public double getPctSmallODE(Object clusterId, double threshold){
           SpatialClusterInfo clusterInfo = this.clusters.get(clusterId);
             double expected = clusterInfo.ode;
             Set memberIds = clusterInfo.memberIds;
             int smallCount=0;
             int i=0;

             for (Iterator iterator = memberIds.iterator(); iterator.hasNext();) {
                 Object mbrId = (Object) iterator.next();
                 double loc_ode = (Double)dataGisdbf.getValue(Colname_LOCROE, Colname_RecordID, mbrId);//get ODE of a place from GIS table
                 if(loc_ode<threshold|| MathUtils.equals(loc_ode,threshold)){
                    smallCount++;
                 }
             }

            double pct = 1.0 * smallCount / memberIds.size();
            if(pct>=1){
                System.out.println("");
            }
            return pct;
        }

            /**
         * return the percentage of the total  number of counties that has ODE< the given <threshold>
         * @param clusterId
         * @param pthreshold   for p-value
         * @return
         */
        public double getPctSignificantODE(Object clusterId){//,double odethreshold,double pthreshold ){
           SpatialClusterInfo clusterInfo = this.clusters.get(clusterId);
             double expected = clusterInfo.ode;
             Set memberIds = clusterInfo.memberIds;
             int smallCount=0;
             int i=0;

             for (Iterator iterator = memberIds.iterator(); iterator.hasNext();) {
                 Object mbrId = (Object) iterator.next();
                 double loc_ode = (Double)dataGisdbf.getValue(Colname_LOCROE, Colname_RecordID, mbrId);//ODE of a place
                 double loc_pvalue = (Double)dataGisdbf.getValue(Colname_PVALUE, Colname_RecordID, mbrId);//ODE of a place
                 //if(loc_ode>odethreshold&&loc_pvalue>pthreshold){
                 if(loc_ode>1.0&&loc_pvalue>0.05){
                    smallCount++;
                 }
             }

            double pct = 1.0 * smallCount / memberIds.size();
            if(pct>=1){
                System.out.println("");
            }
            return pct;
        }

        /**
         * get the weight variance of ODE for all clusters with signfince value <= <pvalue>
         * @param pvalue
         * @return
         */
        public double getODEVarianceOverall(double pvalue){
            Set<SpatialClusterInfo> clusters = getSignificantClusters(pvalue);
            double variance=0;
            int totalcount=0;
            for (Iterator clusterInfoIterator = clusters.iterator(); clusterInfoIterator.hasNext();) {
                SpatialClusterInfo cls = (SpatialClusterInfo) clusterInfoIterator.next();
                double std = cls.variance4ode; //standard deviation for ode
                int count = cls.memberIds.size();
                totalcount=totalcount+count;
                variance=variance+std*count;
            }
            variance=variance/totalcount;
            return variance;
        }
        /**
         * get count of data contained in the clusters with signfince value <= <pvalue>
         * @param pvalue
         * @return
         */
        public int getDataCount(double pvalue){
            Set<SpatialClusterInfo> clusters = getSignificantClusters(pvalue);

            int totalcount=0;
            for (Iterator clusterInfoIterator = clusters.iterator(); clusterInfoIterator.hasNext();) {
                SpatialClusterInfo cls = (SpatialClusterInfo) clusterInfoIterator.next();
               // double std = cls.variance4ode; //standard deviation for ode
                int count = cls.memberIds.size();
                totalcount=totalcount+count;

            }

            return totalcount;
        }
        public int getDataCount(double pvalue, double variance){
            Set<SpatialClusterInfo> clusters = getSignificantClusters(pvalue);

            int totalcount=0;
            for (Iterator clusterInfoIterator = clusters.iterator(); clusterInfoIterator.hasNext();) {
                SpatialClusterInfo cls = (SpatialClusterInfo) clusterInfoIterator.next();
                double std = cls.variance4ode; //standard deviation for ode
               if(std<variance){
                int count = cls.memberIds.size();
                totalcount=totalcount+count;
               }

            }

            return totalcount;
        }

        public  Set <SpatialClusterInfo> getSignificantClusters(double pvalue){
            Set <SpatialClusterInfo> clusters=new HashSet<SpatialClusterInfo>();
            Collection<SpatialClusterInfo> clsinfos = this.clusters.values();
            for (Iterator clusterInfoIterator = clsinfos.iterator(); clusterInfoIterator.hasNext();) {
                SpatialClusterInfo clsinfo = (SpatialClusterInfo) clusterInfoIterator.next();
                if(clsinfo.pvalue<=pvalue){
                    clusters.add(clsinfo);
                }
            }
            return clusters;
        }

      /*  private double calculateStDve4ODE(double[] a){

        }*/



        /**
         *
         * @return     
         */
        public Map<Object, Set>  getClusterGroup(){
            Map<Object, Set> clusterGroup = process(dataGisdbf, Colname_ClusterID, Colname_RecordID);// "CLUSTER");
            return clusterGroup;

        }
      public static Map<Object, Set> process(MyTableModel data, String keycol, String idcol) {

          Map grp=new ListOrderedMap();
          List keycoln=null;
         
          List col=data.getColumn(keycol);
          keycoln=col;
         if(keycoln==null || keycoln.size()==0){
            throw new IllegalArgumentException("Given data don't have name, unable to do observation summary");
        }

         List valueColumn = null;
         if(idcol!=null) {
             valueColumn=data.getColumn(idcol);
         }

         //Do summary
         for (int row =0;row <data.getRowCount() ;row++){
           Object groupId= keycoln.get(row) ;
           if(groupId==null){
            
               continue;
           }
           else{
               if (groupId instanceof String && ((String) groupId).length() ==0 ) {// the value is an empty string
                   continue;
               }
           }
          
          Set memberIds= (Set) grp.get(groupId);
           if(memberIds==null){
               memberIds=new ListOrderedSet();
                   grp.put(groupId , memberIds); //
           }
         
           if(valueColumn==null)
                memberIds.add(row); 
           else{
               Object mbrId = valueColumn.get(row);
               memberIds.add(mbrId);
           }

       }
       return grp;

     }
        /*******************************************************************************************************
         *                get / set
         *******************************************************************************************************/
        public Set getSelClusterIds() {
            if(selClusterIds ==null){
               selClusterIds =new HashSet();
            }
            return selClusterIds;
        }

        public void setSelClusterIds(Set selClusterIds) {
            this.selClusterIds = selClusterIds;
        }

        public Object getSessionId() {
            return sessionId;
        }
        /**
         *  
         *  
         *
         * @param s
         * @return
         */
        public double getSaTScanPopulationSize(){
            return  PerentageComparator.getPercentage( this.getSessionId().toString());
        }
        public Set getDataIds(){
            Set selClusterIds= getSelClusterIds();
            Map<Object, Set> map = getClusterGroup();
            Set mydata = getdata(map, selClusterIds);
            return  mydata;
        }
        public static Set getdata(Map<Object, Set> dataGroups,Set groupIds){
        Set memberIds=new HashSet();
        for (Iterator iterator = groupIds.iterator(); iterator.hasNext();) {
            Object groupId = (Object) iterator.next();
            Set mbrIds = dataGroups.get(groupId) ;  //member ids
            for (Iterator iterator1 = mbrIds.iterator(); iterator1.hasNext();) {
                Object id = (Object) iterator1.next();
                 memberIds.add(id);
                
            }
        }
        return  memberIds;
      }
        public MyTableModel getDataRRdbf() {
            return dataRRdbf;
        }

        public void setDataRRdbf(MyTableModel dataRRdbf) {
            this.dataRRdbf = dataRRdbf;
        }

        public MyTableModel getDataGisdbf() {
            return dataGisdbf;
        }

        public void setDataGisdbf(MyTableModel dataGisdbf) {
            this.dataGisdbf = dataGisdbf;
        }

        public MyTableModel getDataColdbf() {
            return dataColdbf;
        }

        public void setDataColdbf(MyTableModel dataColdbf) {
            this.dataColdbf = dataColdbf;
        }

        public String getFileGisdbf() {
            return fileGisdbf;
        }

        public void setFileGisdbf(String fileGisdbf) {
            this.fileGisdbf = fileGisdbf;
        }

        public boolean isFillPopulationInfo() {
            return fillPopulationInfo;
        }

        public void setFillPopulationInfo(boolean fillPopulationInfo) {
            this.fillPopulationInfo = fillPopulationInfo;
        }


        /*******************************************************************************************************
         *                support/statics
         *******************************************************************************************************/

        /**
         *  
         *
         * @param cmodel
         * @param clusterGroup
         * @param columnName
         * @return
         */
        public  static DefaultTableModel compileClusterTableData(MyTableModel cmodel,Map<Object, Set> clusterGroup,String[] columnNames){
            //Object[] colnames=new Object[]{Colname_ClusterID,Colname_Size};
            //   System.arraycopy(columnName);
            DefaultTableModel tmodel=new DefaultTableModel();
            //tmodel.a
            //1. column names
            Vector colnames=new Vector();
            colnames.add(Colname_ClusterID);
            colnames.add(Colname_Size);
            for (int i=0;i<columnNames.length ;i++){
                colnames.add(columnNames[i]);
            }
            tmodel.setColumnIdentifiers(colnames);

            //2.values
            Set<Object> clusterIds = clusterGroup.keySet();
            for (Iterator iterator = clusterIds.iterator(); iterator.hasNext();) {
                Vector arow=new Vector();
                Object clsId = (Object) iterator.next();
                Set mbrIdSet = clusterGroup.get(clsId);
                int size = mbrIdSet.size();
                // 
                arow.add(clsId);
                arow.add(size);
                // 
                Object fips = ((ListOrderedSet) mbrIdSet).get(0);
                int row = cmodel.getRowIndex(Colname_RecordID, fips); // 
                Object value=null;

                for (int i=0;i<columnNames.length ;i++){
                    if(row>=0){
                        value = cmodel.getValueAt(row, columnNames[i]);
                    }
                    arow.add(value);
                }
                //add the row data to datamodel
                tmodel.addRow(arow);
            }
            return tmodel;

        }
   

        public static class SatScanResultIdComparotor implements Comparator {

            public int compare(Object o1, Object o2) {
                SSResult r1= (SSResult) o1;
                SSResult r2= (SSResult) o2;
                String  sesid1 = (String) r1.sessionId;
                String  sesid2 = (String) r2.sessionId;

                int v = sesid1.compareTo(sesid2);
                return v;  //To change body of implemented methods use File | Settings | File Templates.
            }
        }
         public static class SatScanResultIdPercentageComparotor extends PerentageComparator {

            public int compare(Object o1, Object o2) {
                SSResult r1= (SSResult) o1;
                SSResult r2= (SSResult) o2;
                String  sesid1 = (String) r1.sessionId;
                String  sesid2 = (String) r2.sessionId;

                int v = super.compare(sesid1,sesid2);//sesid1.compareTo(sesid2);
                return v;  //To change body of implemented methods use File | Settings | File Templates.
            }
        }

        /**
         *  
         *  
         */
        public static class PerentageComparator implements Comparator{
            /**
             *
             * @param o1    a
             * @param o2
             * @return
             */
            public int compare(Object o1, Object o2) {

                String n1 = (String) o1;
                String n2 = (String) o2;
                double p1 = getPercentage(n1);
                double p2 = getPercentage(n2);
                return Double.compare(p1,p2);
            }

            public static double  getPercentage(String name){
                StringTokenizer tk=new StringTokenizer(name,"_");
                while(tk.hasMoreTokens()){
                    String s = tk.nextToken();
                    if(
                    NumberUtils.isNumber(s))return Double.parseDouble(s);
                }
                return -1;
            }
        }


        public static void main(String[] args) {
           
        }

    }

